/*
 * Copyright 2023-2023 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package net.yun789.javasipweb.user.context;

import lombok.extern.slf4j.Slf4j;
import net.yun789.javasipweb.dto.LoginUserDTO;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.stereotype.Component;

import javax.sip.address.Address;
import javax.sip.address.URI;
import javax.sip.header.*;
import javax.sip.message.Request;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.stream.Collectors;

/**
 * 登录用户存储默认实现
 * @author ydy
 */
@Component
@Slf4j
public class DefaultUserContextRepository implements UserContextRepository{
    /**
     *登陆用户  key 为callId
     */

    private Map<String, LoginUserDTO> loginUserDTOMap = new ConcurrentHashMap<>(100);

    /**
     * 登陆用户
     * key 为通话地址
     */
    private Map<String, List<LoginUserDTO>> loginUserDTOListMap =new ConcurrentHashMap<>(100);
    @Override
    public LoginUserDTO loadContext(Request request) {
        CallIdHeader callIdHeader = (CallIdHeader) request.getHeader(CallIdHeader.NAME);
        String callId = callIdHeader.getCallId();
        return loginUserDTOMap.get(callId);
    }

    @Override
    public void saveContext(LoginUserDTO loginUser, Request request) {
        CallIdHeader callIdHeader = (CallIdHeader) request.getHeader(CallIdHeader.NAME);
        String callId = callIdHeader.getCallId();
        loginUserDTOMap.put(callId,loginUser);
        FromHeader fromHeader = (FromHeader) request.getHeader(FromHeader.NAME);
        List<LoginUserDTO> loginUserDTOList = this.getLoginUserDTOList(fromHeader.getAddress().getURI().toString());
        loginUserDTOList.add(loginUser);
    }

    @Override
    public boolean containsContext(Request request) {
        CallIdHeader callIdHeader = (CallIdHeader) request.getHeader(CallIdHeader.NAME);
        String callId = callIdHeader.getCallId();
        return this.loginUserDTOMap.containsKey(callId);
    }

    @Override
    public LoginUserDTO loadContext(String uriStr) {
        List<LoginUserDTO> loginUserDTOList = loginUserDTOListMap.get(uriStr);
        if(loginUserDTOList==null||loginUserDTOList.isEmpty()){
            return null;
        }
        return loginUserDTOList.get(loginUserDTOList.size()-1);
    }

    @Override
    public void logOff(Request request) {
        FromHeader fromHeader = (FromHeader) request.getHeader(FromHeader.NAME);
        CallIdHeader callIdHeader = (CallIdHeader) request.getHeader(CallIdHeader.NAME);
        List<LoginUserDTO> loginUserDTOList = loginUserDTOListMap.get(fromHeader.getAddress().getURI().toString());
        loginUserDTOMap.remove(callIdHeader.getCallId());

        log.info("注销前:{}",loginUserDTOList);
        if(loginUserDTOList!=null){
            List<LoginUserDTO> loginUserDTOListTemp = loginUserDTOList.stream().filter(e -> e.getCallId().toString().equals(callIdHeader.getCallId())).collect(Collectors.toList());
            loginUserDTOList.removeAll(loginUserDTOListTemp);
            log.info("注销后:{}",loginUserDTOList);
        }
    }

    @Override
    public boolean login(Request request) {
        AuthorizationHeader authorizationHeader = (AuthorizationHeader) request.getHeader(AuthorizationHeader.NAME);

        if(authorizationHeader==null){
            return false;
        }

        FromHeader head = (FromHeader)request.getHeader(FromHeader.NAME);
//        ContactHeader contactHeader = (ContactHeader) request.getHeader(ContactHeader.NAME);
        Address fromAddress = head.getAddress();
        URI fromURI = fromAddress.getURI();
        CallIdHeader callIdHeader = (CallIdHeader) request.getHeader(CallIdHeader.NAME);


        String ha1WaitHexStr = authorizationHeader.getUsername()+":"+ authorizationHeader.getRealm()+":"+"1234";
        String ha1= DigestUtils.md5Hex(ha1WaitHexStr);
//                        String ha2WaitHexStr=Request.REGISTER+":"+head.getAddress().getURI();
        String ha2WaitHexStr=request.getMethod()+":"+ authorizationHeader.getURI().toString();
        String ha2=DigestUtils.md5Hex(ha2WaitHexStr);
        String nonceCount= authorizationHeader.getNonceCount()+"";
        while(nonceCount.length()<8){
            nonceCount="0"+nonceCount;
        }
        String authResponseServeWaitSignStr=ha1 + ":" + authorizationHeader.getNonce()+":"+nonceCount+":"+ authorizationHeader.getCNonce()+":"+ authorizationHeader.getQop()+ ":" + ha2;
        String authResponseServe = DigestUtils.md5Hex(authResponseServeWaitSignStr);
        String authResponseClient = authorizationHeader.getResponse();
        if(!authResponseServe.equals(authResponseClient)){
//            returnNoAuth(request,401);
            return false;
        }
        ContactHeader contactHeader = (ContactHeader) request.getHeader(ContactHeader.NAME);

        //添加登陆信息
        LoginUserDTO loginUserDTO = new LoginUserDTO();
        loginUserDTO.setCallId(callIdHeader.getCallId());
        loginUserDTO.setUsername(authorizationHeader.getUsername());
        loginUserDTO.setUri(fromURI);
        loginUserDTO.setRealm(authorizationHeader.getRealm());
        loginUserDTO.setLoginTime(LocalDateTime.now());
        loginUserDTO.setContactURI(contactHeader.getAddress().getURI());
        this.saveContext(loginUserDTO,request);
        return true;

    }

    private synchronized List<LoginUserDTO> getLoginUserDTOList(String key) {
        List<LoginUserDTO> loginUserDTOList = loginUserDTOListMap.computeIfAbsent(key, k -> new CopyOnWriteArrayList<>());
        return loginUserDTOList;
    }
}
